home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / Information / Mac Programming Secrets 1.0.1 / Chapter 06 / MouseTracker.c < prev    next >
C/C++ Source or Header  |  1992-05-19  |  10KB  |  305 lines

  1. #include "MouseTracker.h"
  2.  
  3. // Private global used when tracking. When false, the mousedown location
  4. // is used as one of the corners or endpoints of whatever we draw. If
  5. // set to true, the mousedown location is used as the center of what we draw.
  6.  
  7. Boolean        pFromCenter;
  8.  
  9.  
  10. /*******************************************************************************
  11.  
  12.     SketchNewRect
  13.  
  14.     Called when the mouse is clicked in the content area of the window. Using
  15.     the generic TrackMouse routine, sketches the outline of a rectangle.
  16.     Returns the resulting rectangle.
  17.  
  18. *******************************************************************************/
  19. Rect SketchNewRect(Boolean fromCenter)
  20. {
  21.     Point    startPoint;
  22.     Point    endPoint;
  23.     Rect    tempRect;
  24.  
  25.     pFromCenter = fromCenter;
  26.     TrackMouse(nil, RectFeedback, nil, &startPoint, &endPoint);
  27.     AdjustForTrackingFromCenter(&startPoint, endPoint);
  28.     Pt2Rect(startPoint, endPoint, &tempRect);
  29.     return tempRect;
  30. }
  31.  
  32. void RectFeedback(Point anchorPoint, Point currentPoint,
  33.                  Boolean turnItOn, Boolean mouseDidMove)
  34. {
  35.     Rect    tempRect;
  36.  
  37.     if (mouseDidMove) {
  38.         AdjustForTrackingFromCenter(&anchorPoint, currentPoint);
  39.         Pt2Rect(anchorPoint, currentPoint, &tempRect);
  40.         FrameRect(&tempRect);
  41.     }
  42. }
  43.  
  44.  
  45. /*******************************************************************************
  46.  
  47.     SketchNewLine
  48.  
  49.     Called when the mouse is clicked in the content area of the window. Using
  50.     the generic TrackMouse routine, sketches a line. Returns a rectangle
  51.     indicating the endpoints of the line: the TopLeft of the rectangle is one
  52.     endpoint, the BottomRight is the other.
  53.  
  54. *******************************************************************************/
  55. Rect SketchNewLine(Boolean fromCenter)
  56. {
  57.     Point    startPoint;
  58.     Point    endPoint;
  59.     Rect    tempRect;
  60.  
  61.     pFromCenter = fromCenter;
  62.     TrackMouse(nil, LineFeedback, nil, &startPoint, &endPoint);
  63.     AdjustForTrackingFromCenter(&startPoint, endPoint);
  64.     topLeft(tempRect) = startPoint;
  65.     botRight(tempRect) = endPoint;
  66.     return tempRect;
  67. }
  68.  
  69. void LineFeedback(Point anchorPoint, Point currentPoint,
  70.                  Boolean turnItOn, Boolean mouseDidMove)
  71. {
  72.     if (mouseDidMove) {
  73.         AdjustForTrackingFromCenter(&anchorPoint, currentPoint);
  74.         MoveTo(anchorPoint.h, anchorPoint.v);
  75.         LineTo(currentPoint.h, currentPoint.v);
  76.     }
  77. }
  78.  
  79.  
  80. /*******************************************************************************
  81.  
  82.     SketchNewOval
  83.  
  84.     Called when the mouse is clicked in the content area of the window. Using
  85.     the generic TrackMouse routine, sketches the outline of an oval. Returns
  86.     the bounding rectangle of the resulting oval.
  87.  
  88. *******************************************************************************/
  89. Rect SketchNewOval(Boolean fromCenter)
  90. {
  91.     Point    startPoint;
  92.     Point    endPoint;
  93.     Rect    tempRect;
  94.  
  95.     pFromCenter = fromCenter;
  96.     TrackMouse(nil, OvalFeedback, nil, &startPoint, &endPoint);
  97.     AdjustForTrackingFromCenter(&startPoint, endPoint);
  98.     Pt2Rect(startPoint, endPoint, &tempRect);
  99.     return tempRect;
  100. }
  101.  
  102. void OvalFeedback(Point anchorPoint, Point currentPoint,
  103.                  Boolean turnItOn, Boolean mouseDidMove)
  104. {
  105.     Rect    tempRect;
  106.  
  107.     if (mouseDidMove) {
  108.         AdjustForTrackingFromCenter(&anchorPoint, currentPoint);
  109.         Pt2Rect(anchorPoint, currentPoint, &tempRect);
  110.         FrameOval(&tempRect);
  111.     }
  112. }
  113.  
  114.  
  115. /*******************************************************************************
  116.  
  117.     SketchNewRoundRect
  118.  
  119.     Called when the mouse is clicked in the content area of the window. Using
  120.     the generic TrackMouse routine, sketches the outline of a roundRect.
  121.     Returns the bounding rectangle of the resulting roundRect.
  122.  
  123. *******************************************************************************/
  124. Rect SketchNewRoundRect(Boolean fromCenter)
  125. {
  126.     Point    startPoint;
  127.     Point    endPoint;
  128.     Rect    tempRect;
  129.  
  130.     pFromCenter = fromCenter;
  131.     TrackMouse(nil, RoundRectFeedback, nil, &startPoint, &endPoint);
  132.     AdjustForTrackingFromCenter(&startPoint, endPoint);
  133.     Pt2Rect(startPoint, endPoint, &tempRect);
  134.     return tempRect;
  135. }
  136.  
  137. void RoundRectFeedback(Point anchorPoint, Point currentPoint,
  138.                  Boolean turnItOn, Boolean mouseDidMove)
  139. {
  140.     Rect    tempRect;
  141.  
  142.     if (mouseDidMove) {
  143.         AdjustForTrackingFromCenter(&anchorPoint, currentPoint);
  144.         Pt2Rect(anchorPoint, currentPoint, &tempRect);
  145.         FrameRoundRect(&tempRect, 16, 16);
  146.     }
  147. }
  148.  
  149.  
  150. /*******************************************************************************
  151.  
  152.     Macros which are used in the generic TrackMouse routine. We use macros to
  153.     help make the code clearer.
  154.  
  155.     When TrackMouse is called, it is passed the addresses of three callback
  156.     routines. These routines are called in the main loop of TrackMouse.
  157.     However, the pointers can also be NIL, indicating that there is no
  158.     corresponding routine to be called. These macros check to see if the
  159.     callback address is NIL or not. If not, the callback is called back.
  160.  
  161.     In the case of the Feedback callback routine, we also set up the
  162.     appropriate drawing environment.
  163.  
  164. *******************************************************************************/
  165.  
  166. #define CONSTRAINONCE() \
  167.         if (constrainProc) \
  168.             (*constrainProc)(*anchorPoint, *endPoint, &theMouse);
  169.  
  170. #define FEEDBACKONCE(turnItOn, mouseMoved) \
  171.         if (feedbackProc) { \
  172.             PenNormal(); \
  173.             PenPat(qd.gray); \
  174.             PenMode(patXor); \
  175.             (*feedbackProc)(*anchorPoint, *endPoint, turnItOn, mouseMoved); \
  176.         }
  177.  
  178. #define TRACKONCE(phase, mouseMoved) \
  179.         if (trackMouseProc) \
  180.             (*trackMouseProc)(phase, anchorPoint, endPoint, &theMouse, mouseMoved);
  181.  
  182.  
  183. /*******************************************************************************
  184.  
  185.     TrackMouse
  186.  
  187.     Generic mouse tracking routine. This is a stripped down version of MacApp’s
  188.     own core tracking routine. The main difference in our version is that we
  189.     don’t support automatic scrolling of the window if the mouse moves beyond
  190.     its bounds.
  191.  
  192.     TrackMouse is called with the addresses of three callback routines. The
  193.     first callback is the Constrain callback. This function is called to
  194.     perform any constraining or gridding of the mouse location. It is called
  195.     with the current location of the mouse, and is expected to  return a
  196.     modified location. Note that the cursor itself is NOT  constrained (i.e.,
  197.     the mouse doesn’t jump from grid point to grid point like it does when
  198.     moving a HyperCard window horizontally).
  199.  
  200.     The second callback routine is the FeedBack routine. This routine is
  201.     called to perform any drawing that indicates to the user that something
  202.     important is going on. For instance, if you wanted to sketch a rectangle
  203.     in the window like you can in the Finder, you would provide a FeedBack
  204.     routine that would draw the rectangle. Note that this routine is called
  205.     both to draw the feedback and to erase it later.
  206.  
  207.     The third callback is the Track routine. This callback is used to perform
  208.     any other processing that needs to be done in the main tracking loop. For
  209.     instance, something like the Finder could use this routine to highlight
  210.     any icons that are within the currently sketched rectangle.
  211.  
  212.     The basic algorithm of the routine is thus:
  213.  
  214.          1.    The initial mouse location (the anchor point) is determined
  215.             and constrained.
  216.          2.    The Track routine is called with a message indicating that
  217.             tracking is about to start.
  218.          3.    The FeedBack routine is called to draw the initial feedback.
  219.          4.    The next mouse location is determined and constrained.
  220.          5.    We determine if the mouse moved since the last time we
  221.             looked at it. Later, a Boolean indicating whether or not the
  222.             mouse moved will be passed to the Track and Feedback routines
  223.             to help them decide what to do.
  224.          6.    Delay a single tick. This is to give the last drawn feedback
  225.             a chance to show up on the screen.
  226.          7.    Call the FeedBack routine to erase the previous feedback.
  227.          8.    Call the Track routine to allow it to do any other processing.
  228.          9.    Call the FeedBack routine again to draw the new feedback.
  229.         10.    Repeat steps 4-9 until the mouse button is lifted.
  230.         11.    Call the FeedBack routine to erase the final feedback.
  231.         12.    Call the Track routine, telling it that tracking is complete.
  232.  
  233. *******************************************************************************/
  234. void TrackMouse(ConstrainProcPtr constrainProc,
  235.                 FeedbackProcPtr feedbackProc,
  236.                 TrackMouseProcPtr trackMouseProc,
  237.                 Point *anchorPoint,
  238.                 Point *endPoint)
  239. {
  240.     Point    theMouse;
  241.     Boolean    didMove;
  242.     Rect    finalRect;
  243.     long    dummy;
  244.  
  245.     GetMouse(&theMouse);
  246.     *anchorPoint = theMouse;
  247.     *endPoint = theMouse;
  248.     CONSTRAINONCE();
  249.  
  250.     TRACKONCE(trackPress, kMouseMoved);
  251.     FEEDBACKONCE(kTurnItOn, kMouseMoved);
  252.  
  253.     while (StillDown())  {
  254.         GetMouse(&theMouse);
  255.         CONSTRAINONCE();
  256.         didMove = !EqualPt(*endPoint, theMouse);
  257.         Delay(1, &dummy);                /* give it a chance to show up */
  258.         FEEDBACKONCE(kTurnItOff, didMove);
  259.         TRACKONCE(trackMove, didMove);
  260.         *endPoint = theMouse;
  261.         FEEDBACKONCE(kTurnItOn, didMove);
  262.     }
  263.  
  264.     FEEDBACKONCE(kTurnItOff, didMove);
  265.     TRACKONCE(trackRelease, didMove);
  266. }
  267.  
  268.  
  269. /*******************************************************************************
  270.  
  271.     AdjustForTrackingFromCenter
  272.  
  273.     Handy routine that adjusts the anchor point if we want “track from center”
  274.     tracking. Tracking from the center means that we would like the initial
  275.     mouse down location to be the center of whatever shape we are sketching.
  276.     For example, if we had a routine that sketched out an circle, we might
  277.     want to have the initial mouse location be the circle origin and the
  278.     current mouse location determine its radius.
  279.  
  280.     This routine will take the initial and final mouse locations, and convert
  281.     the initial mouse location such that the two points will describe the
  282.     bounding box of the circle, roundRect, or whatever.
  283.  
  284.     +....................+
  285.     .(p2)                .    Here’s a picture to illustrate what we’re
  286.     .                    .    doing. Assume the initial mouse click is
  287.     .                    .    at p0 and that the user drags the mouse to
  288.     .                    .    p1. The anchor and final points now describe
  289.     .         +----------+    the dark bordered rectangle. However, we want
  290.     .         |(p0)      |    p0 to be the center of the rectangle. Calling
  291.     .         |          |    “AdjustForTrackingFromCenter” will convert
  292.     .         |          |    p0 into p2. Now our two points describe the
  293.     .         |          |    larger rectangle.
  294.     +.........+----------+
  295.                           (p1)
  296.  
  297. *******************************************************************************/
  298. void AdjustForTrackingFromCenter(Point *anchorPoint, Point endPoint)
  299. {
  300.     if (pFromCenter) {
  301.         anchorPoint->v -= (endPoint.v - anchorPoint->v);
  302.         anchorPoint->h -= (endPoint.h - anchorPoint->h);
  303.     }
  304. }
  305.